﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.AccessControl;
using System.Threading;
using System.Text.RegularExpressions;

using System.Windows.Forms;
using Microsoft.Win32;
using NetworkServices;

namespace SearchRS
{
    public enum OperationMode
    {
        GUI,
        CLI,
        JDownloadePlugin
    }

    public class SearchRsParams
    {
        public OperationMode OperationMode { get; set; }
        public SearchResults SearchResults { get; set; }
        public string RsCreds { get; set; }
        public DMLinks DmLinks { get; set; }
        public string DestRoot { get; set; }
        public string DlPath { get; set; }
        public ManualResetEvent ShouldStopEvent { get; set; }
    }

    public class SearchRS
    {
        private OperationMode  m_operationMode;
        private SearchResults  m_searchResults;
        private string         m_RsCreds;
        private DMLinks        m_dmLinks;
        private string         m_DestRoot;
        private string         m_DlPath;

        private static readonly object lockObject = new object();

        protected ManualResetEvent ShouldStopEvent { get; set; }
        protected bool ShouldStop()
        {
            return ShouldStopEvent.WaitOne(1, false);
        }

        public SearchRS(SearchRsParams searchRsParams)
        {
            SetSearchParams(searchRsParams);
        }

        public void SetSearchParams(SearchRsParams searchRsParams)
        {
            m_operationMode = searchRsParams.OperationMode;
            m_searchResults = searchRsParams.SearchResults;
            m_RsCreds = searchRsParams.RsCreds;
            m_dmLinks = searchRsParams.DmLinks;
            m_DestRoot = searchRsParams.DestRoot;
            m_DlPath = searchRsParams.DlPath;
            ShouldStopEvent = searchRsParams.ShouldStopEvent;            
        }

        public bool ShouldPickTheLink(string buffer, int startPos, int endPos, string searchStringCleaned, string searchOrder)
        {
            string searchTitleAndLink = buffer.Substring(startPos, endPos - startPos);
            if (String.IsNullOrEmpty(searchTitleAndLink))
                return false;

            if (!searchOrder.Contains("1080") && searchTitleAndLink.Contains("1080"))
                return false;
            if (!searchOrder.Contains("720") && searchTitleAndLink.Contains("720"))
                return false;

            // Make sure all words included in search result
            return Utils.Utils.AllWordsIncluded(searchTitleAndLink, searchStringCleaned);
        }

        public List<string> CleanLinks(string[] rsLinks)
        {
            List<string> links = new List<string>();

            foreach (string rsLink in rsLinks)
            {
                string theLink;

                int spacePos = rsLink.IndexOfAny(new[] { ' ', '<', '\\', '\n', '\r', '&' });

                int rarPos = rsLink.LastIndexOf(".r");
                if (rarPos == -1) rarPos = rsLink.LastIndexOf(".0");
                if (rarPos == -1) rarPos = rsLink.LastIndexOf(".avi");
                if (rarPos == -1) rarPos = rsLink.LastIndexOf(".zip");
                if (rarPos == -1) rarPos = spacePos + 1;

                if (spacePos > 0 && spacePos < rarPos)
                {
                    theLink = rsLink.Substring(0, spacePos);
                }
                else
                {
                    theLink = rsLink.Substring(0, rarPos + 4);
                }
                if (!theLink.Contains("http://www.filesonic.com/"))
                    theLink = "http://www.filesonic.com/" + theLink;

                if (theLink.EndsWith(".nfo") || theLink.EndsWith(".sfv"))
                    continue;

                bool found = false;
                for (int i = 0; i < links.Count; i++)
                {
                    if (links[i] == theLink)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    links.Add(theLink);
                }
            }

            return links;
        }

        public delegate void ValidateLinkDelegate(bool validateLink, object ctx);
        private static Dictionary<string, bool> m_validatedLinks = new Dictionary<string, bool>();

        private void ValidateLink(string link, string rsCreds, ValidateLinkDelegate validateLinkCB, object ctx)
        {
            HttpWebRequest req = NetworkServices.NetworkServices.SendPostRequest("http://www.filesonic.co.il/link-checker", "", "", "links=" + Uri.EscapeDataString(link),
                "http://www.filesonic.co.il/link-checker");
            WebResponse resp = req.GetResponse();
            StreamReader sr = new StreamReader(resp.GetResponseStream());
            string s = sr.ReadToEnd();
            Regex regex = new Regex(@"([0-9]) \/ ([0-9]) Available");
            Match m = regex.Match(s);
            if (m.Success)
            {
                int working_links = int.Parse(m.Groups[1].Value);

                if (working_links > 0)
                {
                    validateLinkCB(true, ctx);
                }
                else
                {
                    validateLinkCB(false, ctx);
                }
            }
            else
            {
                validateLinkCB(false, ctx);
            }
        }

        //private void ValidateLink(string link, string rsCreds, ValidateLinkDelegate validateLinkCB, object ctx)
        //{
        //    bool cachedValue;
        //    if (m_validatedLinks.TryGetValue(link, out cachedValue))
        //    {
        //        validateLinkCB(cachedValue, ctx);
        //        return;
        //    }

        //    AsyncWebRequest request = new AsyncWebRequest();
        //    WebRequest req =
        //        request.SendWebBBRequest(link,
        //                                 "",
        //                                 "",
        //                                 5,
        //                                 "",
        //                                 false,
        //                                 "",
        //                                 null,
        //                                 20,
        //                                 rsCreds,
        //                                 false,
        //                                 delegate(string response, 
        //                                          HttpWebResponse responseObj,
        //                                          string searchCookieParam,
        //                                          object userState)
        //                                     {
        //                                         if (string.IsNullOrEmpty(response))
        //                                         {
        //                                             m_validatedLinks[link] = false;
        //                                             validateLinkCB(false, ctx);
        //                                             return;
        //                                         }

        //                                         if (response.ToLower().Contains("has been blocked") ||
        //                                             response.ToLower().Contains("the file could not be found") ||
        //                                             response.ToLower().Contains("file deleted") ||
        //                                             response.ToLower().Contains("file not found") ||
        //                                             response.ToLower().Contains("download not available") ||
        //                                             response.ToLower().Contains("this file is marked as illegal") ||
        //                                             // filesonic
        //                                             response.ToLower().Contains("this file was deleted")
        //                                             )
        //                                         {
        //                                             m_validatedLinks[link] = false;
        //                                             validateLinkCB(false, ctx);
        //                                             return;
        //                                         }


        //                                         if (response.Substring(0, 3).Equals("Rar"))
        //                                         {
        //                                             m_validatedLinks[link] = true;
        //                                             validateLinkCB(true, ctx);
        //                                             return;
        //                                         }

        //                                         AsyncWebRequest linkedRequest = new AsyncWebRequest();
        //                                         linkedRequest.SendWebBBRequest(responseObj.ResponseUri.AbsoluteUri,
        //                                                                        "",
        //                                                                        "",
        //                                                                        5,
        //                                                                        "",
        //                                                                        false,
        //                                                                        "",
        //                                                                        null,
        //                                                                        20,
        //                                                                        rsCreds,
        //                                                                        false,
        //                                                                        delegate(string linkedResponse,
        //                                                                                 HttpWebResponse linkedresponseObj,
        //                                                                                 string linkedSearchCookieParam,
        //                                                                                 object userState1)
        //                                                                        {
        //                                                                          if (string.IsNullOrEmpty(linkedResponse))
        //                                                                          {
        //                                                                              m_validatedLinks[link] = false;
        //                                                                              validateLinkCB(false, ctx);
        //                                                                              return;
        //                                                                          }

        //                                                                          if (response.ToLower().Contains("has been blocked") ||
        //                                                                              response.ToLower().Contains("the file could not be found") ||
        //                                                                              response.ToLower().Contains("file deleted") ||
        //                                                                              response.ToLower().Contains("download not available") ||
        //                                                                              response.ToLower().Contains("file not found") ||
        //                                                                              response.ToLower().Contains("this file is marked as illegal") ||
        //                                                                              // filesonic
        //                                                                              response.ToLower().Contains("this file was deleted"))

        //                                                                          {
        //                                                                              m_validatedLinks[link] = false;
        //                                                                              validateLinkCB(false, ctx);
        //                                                                              return;
        //                                                                          }

        //                                                                          m_validatedLinks[link] = true;
        //                                                                          validateLinkCB(true, ctx);

        //                                                                        },
        //                                                                        null);
        //                                     },
        //                                 null);
        //}


        public int AddResult(string title, string link, List<string> rsLinks, string subtitleLink, string password, ResultState state)
        {
            // If RS credential exist, validate rs link and turn it to unverifiable if link are invalid
            if (!string.IsNullOrEmpty(m_RsCreds) && rsLinks != null && rsLinks.Count > 0)
            {
                ManualResetEvent finished = new ManualResetEvent(false);
                int res=-1;

                ValidateLink(rsLinks[0], m_RsCreds,
                    delegate(bool validateLink, object ctx)
                        {
                            if (!validateLink)
                                state = ResultState.VerifiedAndEmpty;

                            res = AddResult(rsLinks, state, subtitleLink, password, title, link);
                            finished.Set();
                        },
                        null);

                finished.WaitOne();

                return res;
            }

            return AddResult(rsLinks, state, subtitleLink, password, title, link);
        }

        private int AddResult(List<string> rsLinks, ResultState state, string subtitleLink, string password, string title, string link)
        {
            switch (m_operationMode)
            {
                case OperationMode.CLI:
                    if (state == ResultState.Verified)
                    {
                        AddAllResultsToDmAndExit(rsLinks, subtitleLink, password, title, link);
                    }
                    return 0;

                case OperationMode.GUI:
                case OperationMode.JDownloadePlugin:
                    {
                        return m_searchResults.AddResult(title, link, rsLinks, subtitleLink, password, state);
                    }

                default:
                    throw new ArgumentException("Invalid operation mode " + m_operationMode);
            }
        }

        protected void UpdateResult(int id, List<string> rsLinks, string subtitleLink, string password, ResultState state, string title, string url)
        {
            // If RS credential exist, validate rs link and turn it to unverifiable if link are invalid
            if (!string.IsNullOrEmpty(m_RsCreds) && rsLinks != null && rsLinks.Count > 0)
            {
                ValidateLink(rsLinks[0], m_RsCreds,
                    delegate(bool validateLink, object ctx)
                    {
                        if (!validateLink)
                            state = ResultState.VerifiedAndEmpty;

                        UpdateResult(rsLinks, state, subtitleLink, password, id, title, url);
                    },
                        null);

                return;
            }

            UpdateResult(rsLinks, state, subtitleLink, password, id, title, url);
        }

        private void UpdateResult(List<string> rsLinks, ResultState state, string subtitleLink, string password, int id, string title, string url)
        {
            switch (m_operationMode)
            {
                case OperationMode.CLI:
                    {
                        if (state == ResultState.Verified)
                        {
                            AddAllResultsToDmAndExit(rsLinks, subtitleLink, password, title, url);
                        }
                    }
                    break;

                case OperationMode.GUI:
                case OperationMode.JDownloadePlugin:
                    {
                        m_searchResults.UpdateResult(id, rsLinks, subtitleLink, password, state);
                    }
                    break;

                default:
                    throw new ArgumentException("Invalid operation mode: " + m_operationMode);
            }
        }

        public void AddAllResultsToDm(List<string> rsLinks, string subtitleLink, string password, string title, string url)
        {
            lock (lockObject)
            {
                m_dmLinks.RsLinks = rsLinks;
                m_dmLinks.SubsLink = subtitleLink;
                m_dmLinks.Passowrd = password;
                if (!m_dmLinks.AddLinksAndPasswordToDM())
                {
                    return;
                }

                // Download subtitles
                if (!string.IsNullOrEmpty(m_dmLinks.SubsLink))
                {
                    try
                    {
                        File.AppendAllText("RapidSearcher.log", "Downloading subtitles " + m_dmLinks.SubsLink);
                        WebClient webClient = new WebClient();
                        webClient.DownloadFile(new Uri(m_dmLinks.SubsLink), Path.Combine(m_DlPath,Utils.Utils.FilenameFromTitle(title) + ".zip"));
                        File.AppendAllText("RapidSearcher.log", "Downloaded subtitles " + m_dmLinks.SubsLink + " successfully");
                    }
                    catch (Exception)
                    {
                        if (m_dmLinks.SubsLink.Equals("a"))
                            Console.Write("a");
                        // ignore exception
                    }
                }

                AddPasswordToRegisty(rsLinks[0], password);

                File.AppendAllText("RapidSearcher.log", "Added \"" + title + "\" from url \"" + url + "\"\n");

                if (!string.IsNullOrEmpty(m_DestRoot))
                {
                    string dirName = title;
                    int pos = dirName.IndexOf("(");
                    if (pos >= 0)
                    {
                        dirName = dirName.Substring(0, pos);
                    }
                    dirName = Utils.Utils.FilenameFromTitle(dirName);
                    dirName = m_DestRoot + "\\" + dirName;
                    Directory.CreateDirectory(dirName);
                    string rsFilename = rsLinks[0].Substring(rsLinks[0].LastIndexOf("/") + 1);
                    rsFilename = rsFilename.Substring(0, rsFilename.LastIndexOf("."));
                    File.AppendAllText("DestDirsAndPasswords.txt", rsFilename + "," + dirName + "," + password + "\n");

                    if (!string.IsNullOrEmpty(subtitleLink))
                    {
                        string subsFilename = subtitleLink.Substring(subtitleLink.LastIndexOf("/") + 1);
                        subsFilename = subsFilename.Substring(0, subsFilename.LastIndexOf("."));
                        File.AppendAllText("DestDirsAndPasswords.txt", subsFilename + "," + dirName + "," + "\n");
                    }
                }
            }
        }

        public void AddAllResultsToDmAndExit(List<string> rsLinks, string subtitleLink, string password, string title, string url)
        {
            AddAllResultsToDm(rsLinks, subtitleLink, password, title, url);
            Console.WriteLine("Found: " + rsLinks.Count + " links.");
            Environment.Exit(0);
        }


        public void AddPasswordToRegisty(string link, string password)
        {
            if (string.IsNullOrEmpty(password))
                return;

            try
            {
                // Search entry in registry and add password as description
                string[] files = Registry.CurrentUser.OpenSubKey("Software\\DownloadManager").GetSubKeyNames();
                foreach (string file in files)
                {
                    RegistryKey subkey = Registry.CurrentUser.OpenSubKey("Software\\DownloadManager\\" + file + "\\");
                    string filename = (string)subkey.GetValue("FileName");
                    if (link.ToLower().Contains(filename.ToLower()))
                    {

                        Registry.CurrentUser.OpenSubKey("Software\\DownloadManager\\" + file + "\\",
                                                        RegistryKeyPermissionCheck.ReadWriteSubTree,
                                                        RegistryRights.WriteKey).SetValue("Description", password);
                        break;
                    }
                }

            }
            catch (Exception)
            {
                // do nothing
            }
        }

        public void AddPasswordToPasswordsFile(string pwd)
        {
            string passwordsFilePath = "passwords.txt";
            StreamWriter sw = new StreamWriter(passwordsFilePath, true);
            sw.WriteLine(pwd);
            sw.Close();
        }
    }
}
